#ifndef __XML_SERIALIZABLE_H__
  #define __XML_SERIALIZABLE_H__

#include "serializable.h"
#include "xml_parser.h"
#include "smart_stream.h"
#include "useful_functions.h"

//! An implementation for serializable, which uses an XML file as its storage media

/*!
Any object which is inherited from this class can save its state into a given XML file or load its member
variables from the given XML file.

Implementation date: 2008

Usage:

\code
class my_class : public xml_serializable {

private:
  string application_name;

public:

  const string &get_application_name() const {
    return this->application_name;
  }

  virtual void load() {
    DESERIALIZE(application_name);
  }

  virtual void persist(bool requires_lock = true) {
    SERIALIZE(application_name);
    xml_serializable::persist(requires_lock);
  }

  my_class() : xml_serializable("my_class", "my_xml_file.xml") {
    // nothing to do
  }

  virtual ~my_class() {
    // nothing to do
  }
};
\endcode
*/

class xml_serializable : public serializable {
private:
  xml_node node;
  const char * const xml_file_name;
  bool node_initialized; // node initialized by program
  bool pretty_print;

  void init_xml_node() {
    if (node_initialized == true)
      return;

    node.destroy();
    node.set_name(class_name);
    node_initialized = true;
    loaded = false;
  }

  //! (may throw an exception)
  void re_parse() {
    if (loaded == true)
      return;

    xml_parser parser;
    parser.parse_file(xml_file_name, &node); // may throw an exception
    node_initialized = false;
    loaded = true;
  }

protected:
  //! serializes the given variable
  virtual void serialize(const string &variable_name, const bool value) {
    serialize(variable_name, (value == true) ? string("true") : string("false"));
  }

  //! serializes the given variable
  virtual void serialize(const string &variable_name, const int value) {
    string str_cnv;
    serialize(variable_name, converter::to_string(value, str_cnv));
  }

  //! serializes the given variable
  virtual void serialize(const string &variable_name, const long long value) {
    string str_cnv;
    serialize(variable_name, converter::to_string(value, str_cnv));
  }

  //! serializes the given variable
  virtual void serialize(const string &variable_name, const double value) {
    string str_cnv;
    serialize(variable_name, converter::to_string(value, str_cnv));
  }

  //! serializes the given variable
  virtual void serialize(const string &variable_name, const string &value) {
    init_xml_node();
    node.add_child(new xml_node(variable_name, value));
  }

  //! serializes the given variable
  virtual void serialize(const string &variable_name, const xml_node &value) {
    init_xml_node();
    xml_node *n = new xml_node(value);
    n->set_name(variable_name);
    node.add_child(n);
  }

  //! serializes the given list
  template <class T>
  void serialize(const string &variable_name, const list<T> &values) {
    init_xml_node();

    smart_pointer<xml_node> n1(new xml_node(variable_name, global_empty_string));

    static const string str_item = "item";
    string str_cnv;
    typename std::list<T>::const_iterator end_it = values.end();
    for (typename std::list<T>::const_iterator it = values.begin(); it != end_it; ++it)
      n1->add_child(new xml_node(str_item, converter::to_string(*it, str_cnv)));

    xml_node *n2 = n1.operator->();
    n1.release();
    node.add_child(n2);
  }

  //! deserializes the given variable
  virtual void deserialize(bool &output, const string &variable_name) {
    string str_result;
    deserialize(str_result, variable_name);
    if (str_result == "true")
      output = true;
    else
      output = false;
  }

  //! deserializes the given variable
  virtual void deserialize(int &output, const string &variable_name) {
    string str_result;
    deserialize(str_result, variable_name);
    output = converter::to_int(str_result);
  }

  //! deserializes the given variable
  virtual void deserialize(long long &output, const string &variable_name) {
    string str_result;
    deserialize(str_result, variable_name);
    output = converter::to_long_long(str_result);
  }

  //! deserializes the given variable
  virtual void deserialize(double &output, const string &variable_name) {
    string str_result;
    deserialize(str_result, variable_name);
    output = converter::to_double(str_result);
  }

  //! deserializes the given variable
  virtual void deserialize(string &output, const string &variable_name) {
    re_parse();

    const safe_pointer<xml_node> tmp = node.search_by_tag_name(variable_name);
    if (tmp.is_null() == false)
      output = tmp->get_content();
    else
      output.clear();
  }

  //! deserializes the given variable
  virtual void deserialize(xml_node &output, const string &variable_name) {
    re_parse();

    const safe_pointer<xml_node> tmp = node.search_by_tag_name(variable_name);
    if (tmp.is_null() == false)
      output = *tmp;
    else
      output.destroy();
  }

  //! deserializes the given list
  template <class T>
  void deserialize(list<T> &output, const string &variable_name) {
    re_parse();

    const safe_pointer<xml_node> tmp1 = node.search_by_tag_name(variable_name);
    if (tmp1.is_null() == true)
      output.clear();
    else {
      T tmp3;
      smart_stream ss;

      static const string str_item = "item";

      const std::list<xml_node*> &tmp2 = tmp1->get_children();
      std::list<xml_node*>::const_iterator end_it = tmp2.end();

      for (std::list<xml_node*>::const_iterator it = tmp2.begin(); it != end_it; ++it) {
        if ((*it)->get_name() == str_item) {
          ss.set_buffer((*it)->get_content());
          ss >> tmp3;
          output.push_back(tmp3);
        } // if
      } // for
    } // else
  }

  //! writes the data back to the XML file
  virtual void persist(bool requires_lock = true) {
    if (node_initialized == false)
      return;

    string output;
    node.to_string(output, pretty_print);
    node.destroy();
    node_initialized = false; // it makes sure that recalling the persist method works as expected
    loaded = false;
    binary_write(xml_file_name, output);
  }

  //! constructor
  xml_serializable(const char * const class_name, const char * const xml_file_name, bool pretty_print = false)
    : serializable(class_name)
    , xml_file_name(xml_file_name)
    , node_initialized(false)
    , pretty_print(pretty_print) {
    // nothing to do
  }

  //! destructor
  virtual ~xml_serializable() {
    // nothing to do
  }

public:

};

#endif
